;;; +completion.el --- complete contents, codes & commands -*- lexical-binding: t -*-

;;; Commentary:

;;; Code:

;; optionally use the `orderless' completion style
(use-package orderless
  :custom
  (completion-styles '(orderless basic))
  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles basic partial-completion))))
  (orderless-component-separator #'orderless-escapable-split-on-space))

;; support Pinyin
(use-package pinyinlib
  :after orderless
  :functions orderless-regexp
  :autoload pinyinlib-build-regexp-string
  :init
  (defun orderless-regexp-pinyin (str)
    "Match COMPONENT as a pinyin regex."
    (orderless-regexp (pinyinlib-build-regexp-string str)))
  (add-to-list 'orderless-matching-styles 'orderless-regexp-pinyin))

;; -------- VERTical Interactive COmpletion --------
(use-package vertico
  :custom (vertico-count 15)
  :bind
  (:map vertico-map
        ("RET" . vertico-directory-enter)
        ("DEL" . vertico-directory-delete-char)
        ("M-DEL" . vertico-directory-delete-word))
  :hook
  ((after-init . vertico-mode)
   (rfn-eshadow-update-overlay . vertico-directory-tidy)))

;; Enable rich annotations using the Marginalia package
(use-package marginalia
  :hook
  (after-init . marginalia-mode))

;; Consulting completing-read
(use-package consult
  :hook
  (completion-list-mode . consult-preview-at-point-mode)
  :config
  (defun consult-colors-emacs (color)
    "Show a list of all supported colors for a particular frame.

You can insert the name (default), or insert or kill the hexadecimal or RGB
value of the selected COLOR."
    (interactive
     (list (consult--read (list-colors-duplicates (defined-colors))
                          :prompt "Emacs color: "
                          :require-match t
                          :category 'color
                          :history '(:input consult-colors-history))))
    (insert color))
  :bind
  (([remap Info-search]     . consult-info)
   ([remap isearch-forward] . consult-line) ;; ("C-s")
   ("M-y"                   . consult-yank-pop)
   ("M-g c"                 . consult-colors-emacs)
   ("M-g d"                 . consult-find) ;; search for files in DIR
   ("M-g e"                 . consult-compile-error)
   ("M-g f"                 . consult-flymake)
   ("M-g g"                 . consult-goto-line) ;; orig. goto-line
   ("M-g M-g"               . consult-goto-line) ;; orig. goto-line
   ("M-g i"                 . consult-imenu)
   ("M-g I"                 . consult-imenu-multi)
   ("M-g k"                 . consult-kmacro)
   ("M-g o"                 . consult-outline) ;; jump to an outline heading
   ("M-g T"                 . consult-theme)
   ("C-x b"                 . consult-buffer) ;; orig. switch-to-buffer
   ("M-r"                   . consult-ripgrep)
   ("M-R"                   . consult-grep)))

;; A consulting-read interface for yasnippet
(use-package consult-yasnippet
  :bind ("M-g y" . consult-yasnippet))

;; -------- Emacs Mini-Buffer Actions Rooted in Keymaps --------
(use-package embark
  :commands embark-prefix-help-command
  :bind
  (("C-." . embark-act)  ;; pick some comfortable binding
   ("M-." . embark-dwim) ;; overrides `xref-find-definitions'
   ([remap describe-bindings] . embark-bindings)
   :map minibuffer-local-map
   ("M-." . my-embark-preview))
  :init
  ;; Optionally replace the key help with a completing-read interface
  (setq prefix-help-command #'embark-prefix-help-command)
  :config
  ;; Manual preview for non-Consult commands using Embark
  (defun my-embark-preview ()
    "Previews candidate in vertico buffer, unless it's a consult command."
    (interactive)
    (unless (bound-and-true-p consult--preview-function)
      (save-selected-window
        (let ((embark-quit-after-action nil))
          (embark-dwim)))))

  ;; Hide the mode line of the Embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none))))

  (with-no-warnings
    (with-eval-after-load 'which-key
      (defun embark-which-key-indicator ()
        "An embark indicator that displays keymaps using which-key.
The which-key help message will show the type and value of the
current target followed by an ellipsis if there are further
targets."
        (lambda (&optional keymap targets prefix)
          (if (null keymap)
              (which-key--hide-popup-ignore-command)
            (which-key--show-keymap
             (if (eq (plist-get (car targets) :type) 'embark-become)
                 "Become"
               (format "Act on %s '%s'%s"
                       (plist-get (car targets) :type)
                       (embark--truncate-target (plist-get (car targets) :target))
                       (if (cdr targets) "…" "")))
             (if prefix
                 (pcase (lookup-key keymap prefix 'accept-default)
                   ((and (pred keymapp) km) km)
                   (_ (key-binding prefix 'accept-default)))
               keymap)
             nil nil t (lambda (binding)
                         (not (string-suffix-p "-argument" (cdr binding))))))))

      (setq embark-indicators
            '(embark-which-key-indicator
              embark-highlight-indicator
              embark-isearch-highlight-indicator))

      (defun embark-hide-which-key-indicator (fn &rest args)
        "Hide the which-key indicator immediately when using the completing-read prompter."
        (which-key--hide-popup-ignore-command)
        (let ((embark-indicators
               (remq #'embark-which-key-indicator embark-indicators)))
          (apply fn args)))

      (advice-add #'embark-completing-read-prompter
                  :around #'embark-hide-which-key-indicator))))

;; Consult integration for Embark
(use-package embark-consult
  :bind
  (:map minibuffer-mode-map
        ("C-c C-o" . embark-export))
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))

;; COmpletion in Region FUnction
(use-package corfu
  :autoload (corfu-quit consult-completion-in-region)
  :functions (persistent-scratch-save corfu-move-to-minibuffer)
  :custom
  (corfu-auto t)
  (corfu-auto-prefix 2)
  (corfu-count 12)
  (corfu-preview-current nil)
  (corfu-on-exact-match nil)
  (corfu-auto-delay 0.2)
  (corfu-popupinfo-delay '(0.4 . 0.2))
  (global-corfu-modes '((not erc-mode
                             circe-mode
                             help-mode
                             gud-mode
                             vterm-mode)
                        t))
  :custom-face
  (corfu-border ((t (:inherit region :background unspecified))))
  :bind ("M-/" . completion-at-point)
  :hook ((after-init . global-corfu-mode)
         (global-corfu-mode . corfu-popupinfo-mode)
         (global-corfu-mode . corfu-history-mode))
  :config
  ;;Quit completion before saving
  (add-hook 'before-save-hook #'corfu-quit)
  (advice-add #'persistent-scratch-save :before #'corfu-quit)

  ;; Move completions to minibuffer
  (defun corfu-move-to-minibuffer ()
    (interactive)
    (pcase completion-in-region--data
      (`(,beg ,end ,table ,pred ,extras)
       (let ((completion-extra-properties extras)
             completion-cycle-threshold completion-cycling)
         (consult-completion-in-region beg end table pred)))))
  (keymap-set corfu-map "M-m" #'corfu-move-to-minibuffer)
  (add-to-list 'corfu-continue-commands #'corfu-move-to-minibuffer))

;; -------- Display available keybindings --------
(use-package which-key
  :diminish which-key-mode
  :custom
  (which-key-idle-delay 0.3)
  (which-key-popup-type 'side-window)
  (which-key-side-window-location 'bottom)
  (which-key-show-docstrings t)
  ;; (which-key-max-display-columns 1)
  (which-key-show-prefix 'left)
  ;; (which-key-side-window-max-height 10)
  ;; (which-key-max-description-length 30)
  :config
  (which-key-mode t))

(provide '+completion)

;;; +completion.el ends here
